using System;
using System.Runtime.CompilerServices;
using System.Threading;

//
// * Copyright (c) 2002-2009 "Neo Technology,"
// *     Network Engine for Objects in Lund AB [http://neotechnology.com]
// *
// * This file is part of Neo4j.
// * 
// * Neo4j is free software: you can redistribute it and/or modify
// * it under the terms of the GNU Affero General Public License as
// * published by the Free Software Foundation, either version 3 of the
// * License, or (at your option) any later version.
// * 
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// * GNU Affero General Public License for more details.
// * 
// * You should have received a copy of the GNU Affero General Public License
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
// 
namespace org.neo4j.kernel.impl.transaction
{


	using HeuristicMixedException = javax.transaction.HeuristicMixedException;
	using NotSupportedException = javax.transaction.NotSupportedException;
	using RollbackException = javax.transaction.RollbackException;
	using Status = javax.transaction.Status;
	using SystemException = javax.transaction.SystemException;
	using Transaction = javax.transaction.Transaction;
	using TransactionManager = javax.transaction.TransactionManager;
	using XAException = javax.transaction.xa.XAException;
	using XAResource = javax.transaction.xa.XAResource;

	using ReadOnlyNeoException = org.neo4j.kernel.impl.core.ReadOnlyNeoException;
	using XaResource = org.neo4j.kernel.impl.transaction.xaframework.XaResource;
	using ArrayMap = org.neo4j.kernel.impl.util.ArrayMap;

	internal class ReadOnlyTxManager : TransactionManager
	{
		private static Logger log = Logger.getLogger(typeof(ReadOnlyTxManager).Name);

		private ArrayMap<Thread, ReadOnlyTransactionImpl> txThreadMap;

		private int eventIdentifierCounter = 0;

		private XaDataSourceManager xaDsManager = null;

		internal ReadOnlyTxManager()
		{
		}

		[MethodImpl(MethodImplOptions.Synchronized)]
		internal virtual int getNextEventIdentifier()
		{
			return eventIdentifierCounter++;
		}

		internal virtual void stop()
		{
		}

		internal virtual void init(XaDataSourceManager xaDsManagerToUse)
		{
			this.xaDsManager = xaDsManagerToUse;
			txThreadMap = new ArrayMap<Thread, ReadOnlyTransactionImpl>(5, true, true);
		}

//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public void begin() throws NotSupportedException
		public virtual void begin()
		{
			Thread thread = Thread.CurrentThread;
			ReadOnlyTransactionImpl tx = txThreadMap.get(thread);
			if (tx != null)
			{
				throw new NotSupportedException("Nested transactions not supported");
			}
			tx = new ReadOnlyTransactionImpl(this);
			txThreadMap.put(thread, tx);
		}

//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public void commit() throws RollbackException, HeuristicMixedException, IllegalStateException
		public virtual void commit()
		{
			Thread thread = Thread.CurrentThread;
			ReadOnlyTransactionImpl tx = txThreadMap.get(thread);
			if (tx == null)
			{
				throw new IllegalStateException("Not in transaction");
			}
			if (tx.getStatus() != Status.STATUS_ACTIVE && tx.getStatus() != Status.STATUS_MARKED_ROLLBACK)
			{
				throw new IllegalStateException("Tx status is: " + getTxStatusAsString(tx.getStatus()));
			}
			tx.doBeforeCompletion();
			if (tx.getStatus() == Status.STATUS_ACTIVE)
			{
				commit(thread, tx);
			}
			else if (tx.getStatus() == Status.STATUS_MARKED_ROLLBACK)
			{
				rollbackCommit(thread, tx);
			}
			else
			{
				throw new IllegalStateException("Tx status is: " + getTxStatusAsString(tx.getStatus()));
			}
		}

		private void commit(Thread thread, ReadOnlyTransactionImpl tx)
		{
			if (tx.getResourceCount() == 0)
			{
				tx.setStatus(Status.STATUS_COMMITTED);
			}
			else
			{
				throw new ReadOnlyNeoException();
			}
			tx.doAfterCompletion();
			txThreadMap.Remove(thread);
			tx.setStatus(Status.STATUS_NO_TRANSACTION);
		}

//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: private void rollbackCommit(Thread thread, ReadOnlyTransactionImpl tx) throws HeuristicMixedException, RollbackException
		private void rollbackCommit(Thread thread, ReadOnlyTransactionImpl tx)
		{
			try
			{
				tx.doRollback();
			}
			catch (XAException e)
			{
				e.printStackTrace();
				log.severe("Unable to rollback marked transaction. " + "Some resources may be commited others not. " + "Neo4j kernel should be SHUTDOWN for " + "resource maintance and transaction recovery ---->");
				throw new HeuristicMixedException("Unable to rollback " + " ---> error code for rollback: " + e.errorCode);
			}

			tx.doAfterCompletion();
			txThreadMap.Remove(thread);
			tx.setStatus(Status.STATUS_NO_TRANSACTION);
			throw new RollbackException("Failed to commit, transaction rolledback");
		}

//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public void rollback() throws IllegalStateException, SystemException
		public virtual void rollback()
		{
			Thread thread = Thread.CurrentThread;
			ReadOnlyTransactionImpl tx = txThreadMap.get(thread);
			if (tx == null)
			{
				throw new IllegalStateException("Not in transaction");
			}
			if (tx.getStatus() == Status.STATUS_ACTIVE || tx.getStatus() == Status.STATUS_MARKED_ROLLBACK || tx.getStatus() == Status.STATUS_PREPARING)
			{
				tx.doBeforeCompletion();
				try
				{
					tx.doRollback();
				}
				catch (XAException e)
				{
					e.printStackTrace();
					log.severe("Unable to rollback marked or active transaction. " + "Some resources may be commited others not. " + "Neo4j kernel should be SHUTDOWN for " + "resource maintance and transaction recovery ---->");
					throw new SystemException("Unable to rollback " + " ---> error code for rollback: " + e.errorCode);
				}
				tx.doAfterCompletion();
				txThreadMap.Remove(thread);
				tx.setStatus(Status.STATUS_NO_TRANSACTION);
			}
			else
			{
				throw new IllegalStateException("Tx status is: " + getTxStatusAsString(tx.getStatus()));
			}
		}

		public virtual int getStatus()
		{
			Thread thread = Thread.CurrentThread;
			ReadOnlyTransactionImpl tx = txThreadMap.get(thread);
			if (tx != null)
			{
				return tx.getStatus();
			}
			return Status.STATUS_NO_TRANSACTION;
		}

		public virtual Transaction getTransaction()
		{
			return txThreadMap.get(Thread.CurrentThread);
		}

//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public void resume(Transaction tx) throws IllegalStateException
		public virtual void resume(Transaction tx)
		{
			Thread thread = Thread.CurrentThread;
			if (txThreadMap.get(thread) != null)
			{
				throw new IllegalStateException("Transaction already associated");
			}
			if (tx != null)
			{
				ReadOnlyTransactionImpl txImpl = (ReadOnlyTransactionImpl) tx;
				if (txImpl.getStatus() != Status.STATUS_NO_TRANSACTION)
				{
					txImpl.markAsActive();
					txThreadMap.put(thread, txImpl);
				}
			}
		}

		public virtual Transaction suspend()
		{
			ReadOnlyTransactionImpl tx = txThreadMap.Remove(Thread.CurrentThread);
			if (tx != null)
			{
				tx.markAsSuspended();
			}
			return tx;
		}

//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public void setRollbackOnly() throws IllegalStateException
		public virtual void setRollbackOnly()
		{
			Thread thread = Thread.CurrentThread;
			ReadOnlyTransactionImpl tx = txThreadMap.get(thread);
			if (tx == null)
			{
				throw new IllegalStateException("Not in transaction");
			}
			tx.setRollbackOnly();
		}

		public virtual void setTransactionTimeout(int seconds)
		{
		}

		internal virtual sbyte[] getBranchId(XAResource xaRes)
		{
			if (xaRes is XaResource)
			{
				sbyte[] branchId = ((XaResource) xaRes).getBranchId();
				if (branchId != null)
				{
					return branchId;
				}
			}
			return xaDsManager.getBranchId(xaRes);
		}

		internal virtual XAResource getXaResource(sbyte[] branchId)
		{
			return xaDsManager.getXaResource(branchId);
		}

		internal virtual string getTxStatusAsString(int status)
		{
			switch (status)
			{
				case Status.STATUS_ACTIVE:
					return "STATUS_ACTIVE";
				case Status.STATUS_NO_TRANSACTION:
					return "STATUS_NO_TRANSACTION";
				case Status.STATUS_PREPARING:
					return "STATUS_PREPARING";
				case Status.STATUS_PREPARED:
					return "STATUS_PREPARED";
				case Status.STATUS_COMMITTING:
					return "STATUS_COMMITING";
				case Status.STATUS_COMMITTED:
					return "STATUS_COMMITED";
				case Status.STATUS_ROLLING_BACK:
					return "STATUS_ROLLING_BACK";
				case Status.STATUS_ROLLEDBACK:
					return "STATUS_ROLLEDBACK";
				case Status.STATUS_UNKNOWN:
					return "STATUS_UNKNOWN";
				case Status.STATUS_MARKED_ROLLBACK:
					return "STATUS_MARKED_ROLLBACK";
				default:
					return "STATUS_UNKNOWN(" + status + ")";
			}
		}

		[MethodImpl(MethodImplOptions.Synchronized)]
		public virtual void dumpTransactions()
		{
			IEnumerator <ReadOnlyTransactionImpl> itr = txThreadMap.values().GetEnumerator();
			if (!itr.MoveNext())
			{
				Console.WriteLine("No uncompleted transactions");
				return;
			}
			Console.WriteLine("Uncompleted transactions found: ");
			while (itr.MoveNext())
			{
				Console.WriteLine(itr.Current);
			}
		}

		public virtual int getEventIdentifier()
		{
			TransactionImpl tx = (TransactionImpl) getTransaction();
			if (tx != null)
			{
				return tx.getEventIdentifier();
			}
			return -1;
		}
	}

}